package com.doctcloud.common.webservice.utils;

import com.doctcloud.common.webservice.config.WebserviceProperties;
import com.doctcloud.common.webservice.exception.WebserviceInvokeException;
import org.apache.cxf.Bus;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.jws.WebService;
import java.util.Collection;

/**
 * WebService服务端发布工具类（适配CXF 3.5.x+版本，解决API变更问题）
 * 功能：发布服务、安全关闭服务、标准化路径，适配doctCloud异常规范
 *
 * @author doctcloud samula
 */
@Component
public class WebserviceServerUtils {

    @Autowired
    private Bus cxfBus;

    @Autowired
    private WebserviceProperties webserviceProperties;

    /**
     * 发布WebService服务（自动拼接根路径，适配CXF新版本API）
     *
     * @param serviceImpl 服务实现类（必须加@WebService注解，接口与实现类匹配）
     * @param publishPath 服务子路径（示例：/hello → 完整路径：${cxfPath}/hello）
     * @return 包含「Endpoint实例+服务实现类」的封装对象（便于后续关闭服务时获取信息）
     * @throws WebserviceInvokeException 入参非法、无@WebService注解、发布失败时抛出
     */
    public WebservicePublishResult publishService(Object serviceImpl, String publishPath) {
        // 1. 入参校验（提前拦截无效请求，减少底层报错）
        if (serviceImpl == null) {
            throw new WebserviceInvokeException(
                    "ws.server.publish.param.empty",
                    "发布WebService服务失败：服务实现类不能为空",
                    new IllegalArgumentException("Service implementation is null")
            );
        }
        if (publishPath == null || publishPath.trim().isEmpty()) {
            throw new WebserviceInvokeException(
                    "ws.server.publish.param.empty",
                    "发布WebService服务失败：服务子路径不能为空",
                    new IllegalArgumentException("Publish path is null or empty")
            );
        }
        // 校验实现类是否加@WebService注解（无注解会导致发布后无法访问WSDL）
        if (!serviceImpl.getClass().isAnnotationPresent(WebService.class)) {
            throw new WebserviceInvokeException(
                    "ws.server.publish.no.annotation",
                    String.format("发布WebService服务失败：实现类[%s]未添加@WebService注解",
                            serviceImpl.getClass().getName()),
                    new IllegalArgumentException("Service implementation missing @WebService annotation")
            );
        }

        try {
            // 2. 标准化路径（处理斜杠，确保格式统一："hello"→"/hello"，"/hello"保持不变）
            String normalizedPath = normalizePath(publishPath);
            String fullPublishPath = webserviceProperties.getCxfPath() + normalizedPath;

            // 3. 创建CXF EndpointImpl（新版本API，依赖Bus和服务实现类）
            EndpointImpl endpoint = new EndpointImpl(cxfBus, serviceImpl);

            // 4. 启用日志拦截器（打印SOAP请求/响应，调试用，由配置控制）
            if (webserviceProperties.isEnableLogInterceptor()) {
                endpoint.getInInterceptors().add(new LoggingInInterceptor()); // 接收响应日志
                endpoint.getOutInterceptors().add(new LoggingOutInterceptor()); // 发送请求日志
            }

            // 5. 发布服务（绑定路径，启动监听）
            endpoint.publish(fullPublishPath);

            // 6. 封装结果（记录Endpoint和实现类，避免后续无法获取）
            WebservicePublishResult result = new WebservicePublishResult();
            result.setEndpoint(endpoint);
            result.setServiceImpl(serviceImpl);
            result.setPublishPath(fullPublishPath);

            // 日志提示（运维可感知服务发布状态）
            System.out.printf("WebService服务发布成功：实现类=%s，访问路径=%s，WSDL地址=%s?wsdl%n",
                    serviceImpl.getClass().getName(), fullPublishPath, fullPublishPath);

            return result;
        } catch (Exception e) {
            throw new WebserviceInvokeException(
                    "ws.server.publish.fail",
                    String.format("发布WebService服务失败：实现类=%s，子路径=%s，根路径=%s",
                            serviceImpl.getClass().getName(), publishPath, webserviceProperties.getCxfPath()),
                    e
            );
        }
    }

    /**
     * 关闭WebService服务（适配CXF新版本：仅调用stop()即可释放资源，无需destroy()）
     *
     * @param publishResult 发布服务时返回的封装对象（含Endpoint和实现类信息）
     */
    public void closeService(WebservicePublishResult publishResult) {
        if (publishResult == null || publishResult.getEndpoint() == null) {
            return; // 空对象直接返回，避免空指针
        }

        EndpointImpl endpoint = publishResult.getEndpoint();
        Object serviceImpl = publishResult.getServiceImpl();
        try {
            // 新版本关键：调用stop()即可停止服务并释放资源（destroy()已整合到stop()内部）
            if (endpoint.isPublished()) {
                endpoint.stop();
                System.out.printf("WebService服务已停止：实现类=%s，路径=%s%n",
                        serviceImpl.getClass().getName(), publishResult.getPublishPath());
            }
        } catch (Exception e) {
            throw new WebserviceInvokeException(
                    "ws.server.close.fail",
                    String.format("关闭WebService服务失败：实现类=%s，路径=%s",
                            serviceImpl.getClass().getName(), publishResult.getPublishPath()),
                    e
            );
        }
    }

    /**
     * 标准化路径（处理斜杠，确保与CXF路径规则兼容）
     */
    private String normalizePath(String path) {
        if (path == null || path.trim().isEmpty()) {
            return "";
        }
        String trimmedPath = path.trim();
        return trimmedPath.startsWith("/") ? trimmedPath : "/" + trimmedPath;
    }

    // WebserviceServerUtils 中新增
    /**
     * 批量关闭WebService服务
     * @param publishResults 多个服务的发布结果集合
     */
    public void closeServices(Collection<WebservicePublishResult> publishResults) {
        if (publishResults == null || publishResults.isEmpty()) {
            return;
        }
        for (WebservicePublishResult result : publishResults) {
            closeService(result);
        }
    }

    /**
     * 内部封装类：存储服务发布结果（Endpoint+实现类+路径）
     * 解决：CXF新版本无法通过EndpointImpl获取实现类的问题，主动记录关键信息
     */
    public static class WebservicePublishResult {
        // CXF Endpoint实例（用于关闭服务）
        private EndpointImpl endpoint;
        // 服务实现类（用于日志打印、异常信息）
        private Object serviceImpl;
        // 完整发布路径（如/ws/hello）
        private String publishPath;

        // Getter/Setter（仅保留必要访问权限，避免外部修改）
        public EndpointImpl getEndpoint() {
            return endpoint;
        }

        public void setEndpoint(EndpointImpl endpoint) {
            this.endpoint = endpoint;
        }

        public Object getServiceImpl() {
            return serviceImpl;
        }

        public void setServiceImpl(Object serviceImpl) {
            this.serviceImpl = serviceImpl;
        }

        public String getPublishPath() {
            return publishPath;
        }

        public void setPublishPath(String publishPath) {
            this.publishPath = publishPath;
        }
    }
}